// 将 io输入/输出库引入当前作用域。io 库来自于标准库，也被称为 std：
use std::io;
// 从标准库引入了一个叫做 std::cmp::Ordering 的类型到作用域中。 Ordering 也是一个枚举，不过它的成员是 Less、Greater 和 Equal。这是比较两个值时可能出现的三种结果。
use rand::Rng;
use std::cmp::Ordering;

// fn 语法声明了一个新函数，小括号 () 表明没有参数，大括号 { 作为函数体的开始。
fn main() {
    //  println! 是一个在屏幕上打印字符串的宏：
    println!("Guess the number!");

    // rand::thread_rng 函数提供实际使用的随机数生成器：它位于当前执行线程的本地环境中，并从操作系统获取 seed。
    // 接着调用随机数生成器的 gen_range 方法
    let mut rng = rand::thread_rng();
    let secret_number = rng.gen_range(1..=100);
    // let secret_number = rand::thread_rng();
    /*
    Cargo 有一个很棒的功能是：运行 cargo doc --open 命令来构建所有本地依赖提供的文档，并在浏览器中打开。
    例如，假设你对 rand crate 中的其他功能感兴趣，你可以运行 cargo doc --open 并点击左侧导航栏中的 rand。
    */

    println!("The secret number is: {secret_number}");

    // loop 关键字创建了一个无限循环
    loop {
        println!("Please input your guess.");

        /*  变量可变性 eg */
        // 创建一个 变量（variable）来储存用户输入
        // 在 Rust 中，变量默认是不可变的，这意味着一旦我们给变量赋值，这个值就不再可以修改了。
        // 使用修饰符 mut， 来使一个变量可变

        // 等号（=）告诉Rust我们现在想将某个值绑定在变量上。
        // 等号的右边是 guess 所绑定的值，它是 String::new 的结果，这个函数会返回一个 String 的新实例。
        // String 是一个标准库提供的字符串类型，它是 UTF-8 编码的可增长文本块。
        let mut guess = String::new();
        let apples = 5;
        let mut bananas = 6; // 可变
        bananas = 8;
        println!("Please input your guess. current has : {apples}/{bananas}");

        // std::io::stdin().read_line(&mut guess).expect("Failed to read line");
        io::stdin()
            /*
             * .read_line(&mut guess)，调用 read_line 方法从标准输入句柄获取用户输入, 返回一个类型为 Result 的值。
             * &mut guess 作为参数传递给 read_line() 函数，让其将用户输入储存到这个字符串中
             *
             * & 表示这个参数是一个 引用（reference），它允许多处代码访问同一处数据，而无需在内存中多次拷贝。
             * 变量默认是不可变的。因此，需要写成 &mut guess 来使其可变，而不是 &guess。
             */
            .read_line(&mut guess)
            // 如果 io::Result 实例的值是 Err，expect 会导致程序崩溃，并显示当做参数传递给 expect 的信息。
            // 如果 read_line 方法返回 Err，则可能是来源于底层操作系统错误的结果。
            // 如果 Result 实例的值是 Ok，expect 会获取 Ok 中的值并原样返回。
            .expect("Failed to read line");

        // guess 类型转换。
        // Rust 允许用一个新值来 隐藏 （shadow） guess 之前的值。这个功能常用在需要转换值类型之类的场景。它允许我们复用 guess 变量的名字
        // guess 后面的冒号（:）告诉 Rust 我们指定了变量的类型。
        // Rust 有一些内建的数字类型；u32 是一个无符号的 32 位整型。
        let guess: u32 = match guess
            .trim()
            // 字符串的 parse 方法 将字符串转换成其他类型。这里用它来把字符串转换为数值。
            // parse 方法只有在字符逻辑上可以转换为数字的时候才能工作
            .parse()
        {
            // 忽略非数字的猜测并重新请求数字而不是让程序崩溃
            Ok(n) => n,
            // _ 是一个通配符值，本例中用来匹配所有 Err 值，不管其中有何种信息
            Err(_) => continue,
        };

        // 使用 println! 占位符打印值
        println!("You guessed: {}.", guess);

        // cmp 方法用来比较两个值并可以在任何可比较的值上调用。
        /*
        一个 match 表达式由 分支（arms） 构成。一个分支包含一个 模式（pattern）和表达式开头的值与分支模式相匹配时应该执行的代码。
        Rust 获取提供给 match 的值并挨个检查每个分支的模式。
        match 结构和模式是 Rust 中强大的功能，它体现了代码可能遇到的多种情形，并帮助你确保没有遗漏处理。
        match 表达式会在第一次成功匹配后终止
        */
        match guess.cmp(&secret_number) {
            // 输入的值 小于 secret_number 值
            Ordering::Less => println!("Too small!"),
            // 输入的值 大于 secret_number 值
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                // 退出  loop
                break;
            }
        }
    }
}
